#include <Windows.h>
#include "textureManager.h"
#include <gl\GL.h>


//--------------------------------------------- SetTextureParams ------------------------------------
void TextureManager::SetTextureParams( Texture* T )
{
	//wraping modes
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLint)textureParams.textureWrapModeS );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLint)textureParams.textureWrapModeT );

	//filtering method

	//MAG_FILTER zawsze taki sam
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
	
	//MIN_FILTER w zaleznosci czy teksura ma mipmapy czy nie
	if( T->AreMipmaps() )
	{
		if( textureParams.textureFilter == TextureParams::TF_NEAREST_NEIGHBOUR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
		else
		if( textureParams.textureFilter == TextureParams::TF_BILINEAR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );	
		else
		if( textureParams.textureFilter == TextureParams::TF_TRILINEAR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
	}
	else /*Uwaga! Jesli nie ma mipmap, nie da sie ustawic filtra TF_TRILINEAR!*/
	{
		if( textureParams.textureFilter == TextureParams::TF_NEAREST_NEIGHBOUR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		else
		if( textureParams.textureFilter == TextureParams::TF_BILINEAR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );	
		else
		if( textureParams.textureFilter == TextureParams::TF_TRILINEAR )
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );		/* !! */
	}
}

//---------------------------------------- ActivateTexture ------------------------------------------
bool TextureManager::ActivateTexture( const char* key )
{
	if( key == NULL )
	{
		glBindTexture( GL_TEXTURE_2D, 0 );
		return true;
	}

	string skey( key );

	map< string, Texture* >::iterator it2 = namesMap.find( skey );
	if( it2 != namesMap.end() )
	{
		Texture* pTexture = (*it2).second;

		glBindTexture( pTexture->GetTexType(), pTexture->GetId() );
		SetTextureParams( pTexture );

		return true;
	}

	lastError = "TextureManager::ActivateTexture failed. Failed to find texture named: " + string( key ) + " in container.";
	return false;
}

//----------------------------------------------- ToString ------------------------------------------
string TextureParams::ToString()
{
	string info("");

	if( textureWrapModeS == GL_CLAMP )
		info += "GL_TEXTURE_WRAP_S(textureWrapModeS) = GL_CLAMP\n";
	else
		info += "GL_TEXTURE_WRAP_S(textureWrapModeS) = GL_REPEAT\n";

	if( textureWrapModeT == GL_CLAMP )
		info += "GL_TEXTURE_WRAP_T(textureWrapModeT) = GL_CLAMP\n";
	else
		info += "GL_TEXTURE_WRAP_T(textureWrapModeT) = GL_REPEAT\n";

	if( textureFilter == TF_NEAREST_NEIGHBOUR )
		info += "texture filter(textureFilter) = TF_NEAREST_NEIGHBOUR\n";
	else
	if( textureFilter == TF_BILINEAR )
		info += "texture filter(textureFilter) = TF_BILINEAR\n";
	else
	if( textureFilter == TF_TRILINEAR )
		info += "texture filter(textureFilter) = TF_TRILINEAR\n";

	return info;
} 


//------------------------------------- CreateTex2DNoMipmaps -------------------------------------------------
bool TextureManager::CreateTex2DNoMipmaps( 
 	GLint  		internalFormat,
 	GLsizei  	width,
 	GLsizei  	height,
 	GLint  		border,
 	GLenum  	format,
 	GLenum  	type,
 	const GLvoid * data, 
	const char* name )
{
	
	string sname( name );

	//sprawdzamy czy juz takiej tekstury nie ma
	if( namesMap.count( sname ) == 1 )
	{
		lastError = "TextureManager::CreateTex2D failed. Texture with name: " + string( name ) + " is already in container.";
		return false;
	}

	// pytamy OGL'a czy da rade utworzyc taka teskture?
	glTexImage2D( GL_PROXY_TEXTURE_2D, 0, internalFormat, width, height, border, format, type, NULL );	
	GLint w;
	glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
	if (w==0)
	{
		lastError = "TextureManager::CreateTex2D failed. OGL cannot accomodate this texture (GL_PROXY_TEXTURE_2D failed)";
		return false;
	}

	// generujemy
	unsigned int tHandle;
	glGenTextures(1, &tHandle );
	glBindTexture( GL_TEXTURE_2D, tHandle );
	glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, width, height, border, format, type, data );	

	// dodajemy teksture do pojemnika
	Texture* pTexture = new Texture;
	pTexture->id = tHandle;
	pTexture->width = width;
	pTexture->height = height;
	pTexture->texType = GL_TEXTURE_2D;
	pTexture->colorType = internalFormat;
	pTexture->areMipmaps = false;

	namesMap.insert( pair< string, Texture* >( sname, pTexture ) );

	//ustawiamy parametry dla tekstury
	SetTextureParams( pTexture );

	return true;
}
//------------------------------------- LoadTexture2DFromDisk ---------------------------------------
bool TextureManager::LoadTex2DFromDisk( const char* path, int force_channels, unsigned int flags, const char* name )
{
	string spath( path );
	string sname( name );

	//sprawdzamy czy juz takiej tekstury nie ma
	if( namesMap.count( sname ) == 1 )
	{
		lastError = "TextureManager::LoadTex2DFromDisk failed. Texture with name: " + string( name ) + " is already in container.";
		return false;
	}

	//ladujemy za pomoca SOIL'a
	unsigned int tId = SOIL_load_OGL_texture(path, force_channels, SOIL_CREATE_NEW_ID, flags );

	if( tId == 0 )
	{
		lastError = "TextureManager::LoadTex2DFromDisk failed. Internal error (SOIL_load_OGL_texture returned 0).";
		return false;
	}

	//jesli udalo sie zaladowac tworzymy nowa teksture i wypelniamy jej pola
	Texture* pTexture = new Texture;

	pTexture->id = tId;
	glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &(pTexture->width) );
	glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &(pTexture->height) );
	glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &(pTexture->colorType) );
	pTexture->texType = GL_TEXTURE_2D; //narazie tylko tesktury 2d
	//pTexture->
	if( (flags & SOIL_FLAG_MIPMAPS) == SOIL_FLAG_MIPMAPS )
		pTexture->areMipmaps = true;
	else
		pTexture->areMipmaps = false;

	//wkladamy wskaznik do mapy
	namesMap.insert( pair< string, Texture* >( sname, pTexture ) );

	//bindujemy teskture (soil od razu to robi - nie trzeba)
	//glBindTexture( pTexture->GetTexType(), pTexture->GetId() );

	//ustawiamy parametry dla tekstury
	SetTextureParams( pTexture );

	return true;
}
//---------------------------------------------- DeleteAll -----------------------------------------
void TextureManager::DeleteAll()
{
	map< string, Texture* >::iterator it;
	for( it = namesMap.begin(); it != namesMap.end(); ++it )
	{
		unsigned int tId = (unsigned int)((*it).second->GetId());

		// usuwamy dane tekstury po stronie OGL'a
		glDeleteTextures( 1, &tId );

		// czyscimy pointer
		delete ((*it).second);
		(*it).second = NULL;
	}

	namesMap.clear();
}
//---------------------------------------------- DeleteResource --------------------------------------
const bool TextureManager::DeleteResource( const char* key )
{
	typedef map< string, Texture* >::iterator It;

	string skey = string( key );

	// Jesli key to nazwa modelu
	It it = namesMap.find( skey );
	if( it != namesMap.end() )
	{
		unsigned int tId = (unsigned int)((*it).second->GetId());

		// usuwamy dane tekstury po stronie OGL'a
		glDeleteTextures( 1, &tId );

		// czyscimy pointer
		delete ((*it).second);
		(*it).second = NULL;
	}
	else
	{
		lastError = "TextureManager::DeleteResource failed. Couldn't find texture named " + string( key ) + " in container.";
		return false;
	}

	// usuwamy wpis w namesMap 
	namesMap.erase( key );
	return true;
}